package io.kafka101.clickstream.rest.proxy.client;
import com.fasterxml.jackson.core.JsonProcessingException;
import io.kafka101.clickstream.rest.proxy.client.dto.PublishingData;
import io.kafka101.clickstream.rest.proxy.client.dto.PublishingResponse;
import io.kafka101.clickstream.rest.proxy.client.dto.Record;
import io.kafka101.clickstream.rest.proxy.client.util.HttpCallback;
import io.kafka101.clickstream.rest.proxy.client.util.KafkaContentType;
import org.apache.avro.Schema;
import org.apache.avro.reflect.ReflectData;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHeaders;
import org.apache.http.HttpResponse;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.concurrent.FutureCallback;
import org.apache.http.entity.ContentType;
import org.apache.http.entity.StringEntity;
import org.apache.http.message.BasicHeader;
import org.apache.http.nio.client.HttpAsyncClient;
import java.io.UnsupportedEncodingException;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Map;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ConcurrentHashMap;
public class Producer extends AbstractClient {
private final Map<Class<?>, Schema> schemaCache = new ConcurrentHashMap<>();
public Producer(String topics, HttpAsyncClient httpClient) throws URISyntaxException {
super(topics, httpClient);
}
public Producer(URI topics, HttpAsyncClient httpClient) {
super(topics, httpClient);
}
public <T> CompletableFuture<PublishingResponse> publish(T message, String topic)
throws JsonProcessingException, UnsupportedEncodingException {
Schema schema = namespacelessSchemaFor(message.getClass());
return publish(schema, message, topic);
}
public <T> CompletableFuture<PublishingResponse> publish(Schema schema, T message, String topic)
throws JsonProcessingException, UnsupportedEncodingException {
PublishingData data = new PublishingData(new Record(message), null, null, schema.toString(), null);
HttpCallback<PublishingResponse> callback = new HttpCallback(PublishingResponse.class);
executePost(baseUri.resolve(topic),
new StringEntity(mapper.writeValueAsString(data), ContentType.APPLICATION_JSON),
callback);
return callback;
}
private void executePost(URI topic, HttpEntity entity, FutureCallback<HttpResponse> callback) {
HttpPost post = new HttpPost(topic);
post.addHeader(new BasicHeader(HttpHeaders.CONTENT_TYPE, KafkaContentType.avro()));
post.addHeader(new BasicHeader(HttpHeaders.ACCEPT, KafkaContentType.allTypes()));
post.setEntity(entity);
httpClient.execute(post, callback);
}
private Schema namespacelessSchemaFor(Class<?> type) {
return schemaCache.computeIfAbsent(type, clazz -> {
Schema schema = ReflectData.get().getSchema(clazz);
// kind of a hack to set an empty namespace :)
return new Schema.Parser().parse(schema.toString().replace(schema.getNamespace(), ""));
});
}
}